/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014-2020 @ ShenZhen ,China
*******************************************************************************/

#include "Coroutine.h"
#include "Tracer.h"
#include "DateTime.h"
using namespace std;
namespace libemb{

static int s_coroutineStartID=0;
Coroutine::Coroutine()
{
	m_state = STATE_STOP;
	m_id = s_coroutineStartID;
	s_coroutineStartID++;
	m_stackSize = 0;
	m_stackBuffer = NULL;
}

Coroutine::~Coroutine()
{
	delete [] m_stackBuffer;
}

int Coroutine::coroutineID()
{
	return m_id;
}

int Coroutine::state()
{
	return m_state;
}

void Coroutine::usleep(int us)
{
	Time startTime = Time::currentTimeMonotonic();
	if (us<=0)
	{
		us = 1;
	}
	while(1)
	{
		yield();
		Time duration = Time::currentTimeMonotonic()-startTime;
		if (duration.toMicroSeconds()>=us)
		{
			break;
		}
	}
}

void Coroutine::msleep(int ms)
{
	usleep(1000*ms);
}

bool Coroutine::isRunning()
{
	return (m_state==STATE_STOP)?false:true;
}


void Coroutine::resume()
{
	if(m_state==STATE_SUSPEND)
	{
		restoreStack();
		m_state = STATE_START;
		/* 切换到当前协程 */
		if(swapcontext((ucontext_t*)m_scheduler->context(),&m_context)<0)
		{
			TRACE_ERR_CLASS("swap context error:%s\n",ERROR_STRING);
		}
	}
}

void Coroutine::yield()
{
	if(m_state==STATE_START)
	{
		saveStack();
		m_state = STATE_SUSPEND;
		/* 切换回调度线程 */
		if (swapcontext(&m_context,(ucontext_t*)m_scheduler->context())<0)
		{
			TRACE_ERR_CLASS("swap context error:%s\n",ERROR_STRING);
		}
	}
}

void Coroutine::saveStack()
{
	/* 计算当前堆栈位置,堆栈是往下生长的,栈顶在低地址,栈底是高地址 */
	char* stackBottom = m_scheduler->stack()+$COROUTINE_STACK_SIZE;
	char dumy = 0;
	int stackSize = stackBottom-&dumy;/* stackSize:当前所占用的栈空间 */
	//TRACE_REL_CLASS("Coroutine:%d, stack size:%d\n",m_id,stackSize);
	if(stackSize > $COROUTINE_STACK_SIZE)
	{
		TRACE_ERR_CLASS("stack overflow at coroutine:%d\n",m_id);
		return;
	}
	/* 第一次就分配足够的空间 */
	if (m_stackBuffer==NULL)
	{
		m_stackBuffer = new char[$COROUTINE_STACK_SIZE];
	}
	m_stackSize = stackSize;
	memcpy(m_stackBuffer, &dumy, m_stackSize);
}

void Coroutine::restoreStack()
{
	char* stackTop = m_scheduler->stack()+$COROUTINE_STACK_SIZE-m_stackSize;
	memcpy(stackTop, m_stackBuffer, m_stackSize);
}

CoScheduler::CoScheduler()
{

}

CoScheduler::~CoScheduler()
{

}

bool CoScheduler::start(Coroutine* coroutine)
{
	int id = coroutine->m_id;
	int state = coroutine->m_state;
	if (id<0 || state!=Coroutine::STATE_STOP)
	{
		return false;
	}

	CoroutineMap::iterator iter = m_coroutineMap.find(id);
	if (iter!=m_coroutineMap.end())
	{
		m_coroutineMap.erase(iter);
	}
	m_coroutineMap.insert(std::make_pair(id,coroutine));

	/* 创建上下文 */
	if(getcontext(&(coroutine->m_context))<0)
	{
		return false;
	}
	coroutine->m_scheduler = this;
	coroutine->m_context.uc_stack.ss_sp = m_stack;
	coroutine->m_context.uc_stack.ss_size = $COROUTINE_STACK_SIZE;
	coroutine->m_context.uc_stack.ss_flags = 0;
	coroutine->m_context.uc_link = &m_context;

	#if 0
	uint32 p1 = (uint32)((0x00000000FFFFFFFF)&((uint64)(coroutine))>>32);
	uint32 p2 = (uint32)(0x00000000FFFFFFFF&(uint64)(coroutine));
	makecontext(&m_context,(void(*)())coroutineEntry,2,p1,p2);
	#else
	makecontext(&(coroutine->m_context),(void(*)())coroutineEntry,1,(void*)coroutine);
	#endif

	/* 调用swapcontext成功后会跳转到coroutine,不会立即返回,当coroutine挂起后才返回 */
	if(swapcontext(&m_context,&(coroutine->m_context))<0)
	{
		TRACE_ERR_CLASS("swap context error:%s\n",ERROR_STRING);
		return false;
	}
	return true;
}

void CoScheduler::schedule(int coroutineID)
{
	CoroutineMap::iterator iter = m_coroutineMap.find(coroutineID);
	if (iter!=m_coroutineMap.end())
	{
		iter->second->resume();
	}
}
void CoScheduler::stop(int coroutineID)
{
	CoroutineMap::iterator iter = m_coroutineMap.find(coroutineID);
	if (iter!=m_coroutineMap.end())
	{
		iter->second->m_state = Coroutine::STATE_STOP;/* 设置为STATE_STOP,这样协程能正常退出 */
		m_coroutineMap.erase(iter);
	}
}
int CoScheduler::routines()
{
	int count = 0;
	CoroutineMap::iterator iter;
	for (iter = m_coroutineMap.begin(); iter != m_coroutineMap.end(); iter++) 
	{
		if (iter->second->state() == Coroutine::STATE_STOP) 
		{
			m_coroutineMap.erase(iter);
		}
		else
		{
			count++;
		}
	}
	return count;
}

void* CoScheduler::context()
{
	return ((void*)&m_context);
}

char* CoScheduler::stack()
{
	return m_stack;
}

void CoScheduler::coroutineEntry(void* coroutine)
{
	Coroutine* crtn = (Coroutine*)coroutine;
	crtn->m_state = Coroutine::STATE_START;
	crtn->routine();
	while(crtn->m_state!=Coroutine::STATE_STOP)
	{
		crtn->usleep(100);
	}
	crtn->m_state = Coroutine::STATE_STOP;
}

/* 实现自动调度 */
void CoScheduler::run()
{
	while(isRunning())
	{
		CoroutineMap::iterator iter;
		for(iter=m_coroutineMap.begin(); iter!=m_coroutineMap.end(); iter++)
		{
			iter->second->resume();
		}
	}
}



}
